package org.eliza.base.reflectx

import java.io.ByteArrayOutputStream
import java.io.ObjectOutputStream
import java.io.Serializable
import kotlin.reflect.full.*

private  fun main() {

    KClassInfo(1)
}

fun KClassInfo(a: Any) {
    val kClass = a::class
    println(String.format("%30s :${kClass.simpleName}", "kClass.simpleName"))
    println(String.format("%30s :${kClass.qualifiedName}", "kClass.qualifiedName"))
    println(String.format("%30s :${kClass.members}", "kClass.members"))
    println(String.format("%30s :${kClass.constructors}", "kClass.constructors"))
    println(String.format("%30s :${kClass.nestedClasses}", "kClass.nestedClasses"))
    println(String.format("%30s :${kClass.objectInstance}", "kClass.objectInstance"))
    println(String.format("%30s :${kClass.typeParameters}", "kClass.typeParameters"))
    println(String.format("%30s :${kClass.supertypes}", "kClass.supertypes"))
    println(String.format("%30s :${kClass.sealedSubclasses}", "kClass.sealedSubclasses"))
    println(String.format("%30s :${kClass.visibility}", "kClass.visibility"))
    println(String.format("%30s :${kClass.isFinal}", "kClass.isFinal"))
    println(String.format("%30s :${kClass.isOpen}", "kClass.isOpen"))
    println(String.format("%30s :${kClass.isAbstract}", "kClass.isAbstract"))
    println(String.format("%30s :${kClass.isSealed}", "kClass.isSealed"))
    println(String.format("%30s :${kClass.isData}", "kClass.isData"))
    println(String.format("%30s :${kClass.isInner}", "kClass.isInner"))
    println(String.format("%30s :${kClass.isCompanion}", "kClass.isCompanion"))
    println(String.format("%30s :${kClass.isFun}", "kClass.isFun"))
    println(String.format("%30s :${kClass.isValue}", "kClass.isValue"))
    println(String.format("%30s :${kClass.declaredMembers}", "kClass.declaredMembers"))
//   println(String.format("%30s ${value))

    println(String.format("%30s :${kClass.companionObject}", "kClass.companionObject"))
    println(String.format("%30s :${kClass.companionObjectInstance}", "kClass.companionObjectInstance"))
    println(String.format("%30s :${kClass.defaultType}", "kClass.defaultType"))
    println(String.format("%30s :${kClass.declaredMembers}", "kClass.declaredMembers"))
    println(String.format("%30s :${kClass.functions}", "kClass.functions"))
    println(String.format("%30s :${kClass.staticFunctions}", "kClass.staticFunctions"))
    println(String.format("%30s :${kClass.memberFunctions}", "kClass.memberFunctions"))
    println(String.format("%30s :${kClass.memberExtensionFunctions}", "kClass.memberExtensionFunctions"))
    println(String.format("%30s :${kClass.declaredFunctions}", "kClass.declaredFunctions"))
    println(String.format("%30s :${kClass.declaredMemberFunctions}", "kClass.declaredMemberFunctions"))
    println(
        String.format(
            "%30s :${kClass.declaredMemberExtensionFunctions}",
            "kClass.declaredMemberExtensionFunctions"
        )
    )
    println(String.format("%30s :${kClass.staticProperties}", "kClass.staticProperties"))
    println(String.format("%30s :${kClass.superclasses}", "kClass.superclasses"))
    println(String.format("%30s :${kClass.allSupertypes}", "kClass.allSupertypes"))
    println(String.format("%30s :${kClass.allSuperclasses}", "kClass.allSuperclasses"))
//   println(String.format("%30s :${kClass.  isSubclassOf}","kClass.isSubclassOf"))
//   println(String.format("%30s :${kClass.  isSuperclassOf}","kClass.isSuperclassOf"))

}

fun ObjectSize(a: Any) {
    val baos = ByteArrayOutputStream()
    val oos = ObjectOutputStream(baos)
    oos.writeObject(a)
    oos.close()
    val bytes = baos.toByteArray()
    var i = bytes.size - 1
    println(bytes.toString())
    println(i)
}


fun getFirstObjectReference(o: Any): Any {
    val objectType = o.javaClass.typeName
    println(objectType)
    if (isArray(objectType)) {
        try {
            return when (objectType) {
                "java.lang.Integer[]" -> (o as Array<Int>)[0]
                "java.lang.String[]" -> (o as Array<String>)[0]
                "java.lang.Object[]" -> (o as Array<Any>)[0]
                "java.lang.Boolean[]" -> (o as Array<Boolean>)[0]
                else -> (o as Array<Any>)[0]
            }
        } catch (e: IndexOutOfBoundsException) {
        }
    }
    return o
}

fun isArray(objectType: String) =
    objectType.substring(objectType.length - 2) == "[]"

fun getObjectSizeInBytes(o: Any): Int {
    val STRING_JAVA_TYPE_NAME = "java.lang.String"

    val objectType = o.javaClass.typeName
    val isArray = isArray(objectType)
    val objRef = getFirstObjectReference(o)
    if (objRef != null && objRef !is Serializable)
        throw RuntimeException("Object must be serializable for measuring it's memory footprint using this method !")
    try {
        val baos = ByteArrayOutputStream()
        val oos = ObjectOutputStream(baos)
        oos.writeObject(o)
        oos.close()
        val bytes = baos.toByteArray()
        var i = bytes.size - 1
        var j = 0
        while (i != 0) {
            if (objectType !== STRING_JAVA_TYPE_NAME) {
                if (bytes[i].toInt() == 112)
                    return if (isArray)
                        j - 4
                    else
                        j
            } else {
                if (bytes[i].toInt() == 0)
                    return j - 1
            }
            i--
            j++
        }
    } catch (e: Exception) {
        return -1
    }

    return -1

}

fun JClassInfo(a: Any) {
    val kClass = a::class.java
    println(String.format("%30s :${kClass.name}", "kClass.simpleName"))
    println(String.format("%30s :${kClass.simpleName}", "kClass.simpleName"))
    println(String.format("%30s :${kClass.signers}", "kClass.signers"))
    println(String.format("%30s :${kClass.module}", "kClass.module"))
    println(String.format("%30s :${kClass.classLoader}", "kClass.classLoader"))
    println(String.format("%30s :${kClass.packageName}", "kClass.packageName"))
    println(String.format("%30s :${kClass.componentType}", "kClass.componentType"))
    println(String.format("%30s :${kClass.enumConstants}", "kClass.enumConstants"))
}